/*
 * contrib/btree_gist/btree_bit.c
 */
#include "postgres.h"

#include "btree_gist.h"
#include "btree_utils_var.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/varbit.h"


/*
** 位操作
*/
PG_FUNCTION_INFO_V1(gbt_bit_compress);
PG_FUNCTION_INFO_V1(gbt_bit_union);
PG_FUNCTION_INFO_V1(gbt_bit_picksplit);
PG_FUNCTION_INFO_V1(gbt_bit_consistent);
PG_FUNCTION_INFO_V1(gbt_bit_penalty);
PG_FUNCTION_INFO_V1(gbt_bit_same);


/* 定义比较 */

static bool fc_gbt_bitgt(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(bitgt,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_bitge(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(bitge,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_biteq(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(biteq,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_bitle(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(bitle,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_bitlt(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(bitlt,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static int32 fc_gbt_bitcmp(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetInt32(DirectFunctionCall2(byteacmp,
											 PointerGetDatum(fc_a),
											 PointerGetDatum(fc_b)));
}


static bytea * fc_gbt_bit_xfrm(bytea *fc_leaf)
{
	bytea	   *fc_out = fc_leaf;
	int			fc_sz = VARBITBYTES(fc_leaf) + VARHDRSZ;
	int			fc_padded_sz = INTALIGN(fc_sz);

	fc_out = (bytea *) palloc(fc_padded_sz);
	/* 将填充字节初始化为零 */
	while (fc_sz < fc_padded_sz)
		((char *) fc_out)[fc_sz++] = 0;
	SET_VARSIZE(fc_out, fc_padded_sz);
	memcpy((void *) VARDATA(fc_out), (void *) VARBITS(fc_leaf), VARBITBYTES(fc_leaf));
	return fc_out;
}




static GBT_VARKEY * fc_gbt_bit_l2n(GBT_VARKEY *fc_leaf, FmgrInfo *fc_flinfo)
{
	GBT_VARKEY *fc_out = fc_leaf;
	GBT_VARKEY_R fc_r = gbt_var_key_readable(fc_leaf);
	bytea	   *fc_o;

	fc_o = fc_gbt_bit_xfrm(fc_r.lower);
	fc_r.upper = fc_r.lower = fc_o;
	fc_out = gbt_var_key_copy(&fc_r);
	pfree(fc_o);

	return fc_out;
}

static const gbtree_vinfo tinfo =
{
	gbt_t_bit,
	0,
	true,
	fc_gbt_bitgt,
	fc_gbt_bitge,
	fc_gbt_biteq,
	fc_gbt_bitle,
	fc_gbt_bitlt,
	fc_gbt_bitcmp,
	fc_gbt_bit_l2n
};


/**************************************************
 * 位操作
 **************************************************/

Datum gbt_bit_compress(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);

	PG_RETURN_POINTER(gbt_var_compress(fc_entry, &tinfo));
}

Datum gbt_bit_consistent(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);
	void	   *fc_query = (void *) DatumGetByteaP(PG_GETARG_DATUM(1));
	StrategyNumber fc_strategy = (StrategyNumber) PG_GETARG_UINT16(2);

	/* Oid		subtype = PG_GETARG_OID(3); */
	bool	   *fc_recheck = (bool *) PG_GETARG_POINTER(4);
	bool		fc_retval;
	GBT_VARKEY *fc_key = (GBT_VARKEY *) DatumGetPointer(fc_entry->key);
	GBT_VARKEY_R fc_r = gbt_var_key_readable(fc_key);

	/* 由此函数服务的所有案例都是精确的 */
	*fc_recheck = false;

	if (GIST_LEAF(fc_entry))
		fc_retval = gbt_var_consistent(&fc_r, fc_query, fc_strategy, PG_GET_COLLATION(),
									true, &tinfo, fcinfo->flinfo);
	else
	{
		bytea	   *fc_q = fc_gbt_bit_xfrm((bytea *) fc_query);

		fc_retval = gbt_var_consistent(&fc_r, fc_q, fc_strategy, PG_GET_COLLATION(),
									false, &tinfo, fcinfo->flinfo);
	}
	PG_RETURN_BOOL(fc_retval);
}



Datum gbt_bit_union(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	int32	   *fc_size = (int *) PG_GETARG_POINTER(1);

	PG_RETURN_POINTER(gbt_var_union(fc_entryvec, fc_size, PG_GET_COLLATION(),
									&tinfo, fcinfo->flinfo));
}


Datum gbt_bit_picksplit(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	GIST_SPLITVEC *fc_v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);

	gbt_var_picksplit(fc_entryvec, fc_v, PG_GET_COLLATION(),
					  &tinfo, fcinfo->flinfo);
	PG_RETURN_POINTER(fc_v);
}

Datum gbt_bit_same(PG_FUNCTION_ARGS)
{
	Datum		fc_d1 = PG_GETARG_DATUM(0);
	Datum		fc_d2 = PG_GETARG_DATUM(1);
	bool	   *fc_result = (bool *) PG_GETARG_POINTER(2);

	*fc_result = gbt_var_same(fc_d1, fc_d2, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo);
	PG_RETURN_POINTER(fc_result);
}


Datum gbt_bit_penalty(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_o = (GISTENTRY *) PG_GETARG_POINTER(0);
	GISTENTRY  *fc_n = (GISTENTRY *) PG_GETARG_POINTER(1);
	float	   *fc_result = (float *) PG_GETARG_POINTER(2);

	PG_RETURN_POINTER(gbt_var_penalty(fc_result, fc_o, fc_n, PG_GET_COLLATION(),
									  &tinfo, fcinfo->flinfo));
}
